1。なぜ - 一般的なメカニズムを導入する理由
文字列アレイを実装し、サイズを動的に変更するために必要な場合は、arrayListを使用して文字列オブジェクトを集約することを考えます。ただし、しばらくすると、サイズを変更できる日付オブジェクトの配列を実装したいと考えています。現時点では、私が以前に書いた文字列オブジェクトのアレイリスト実装を再利用できることを確かに望んでいます。
Java 5以前は、ArrayListの実装は次のとおりです。
public class arraylist {public object get(int i){...} public void add(object o){...} ... private object [] elementData;}上記のコードから、Add関数がArrayListに要素を追加するために使用される機能が、オブジェクト型パラメーターを受信することがわかります。 ArrayListから指定された要素を取得するGETメソッドも、オブジェクトタイプのオブジェクトを返します。オブジェクトオブジェクト配列elementDataは、アレイリストにオブジェクトを保存します。つまり、ArrayListにどのようなタイプのタイプを入れても、それはその中のオブジェクトオブジェクトです。
継承に基づいた一般的な実装は、2つの問題をもたらします。最初の質問は、GETメソッドに関するものです。 GETメソッドを呼び出すたびに、オブジェクトオブジェクトを返します。たびに、必要なタイプに型をキャストする必要があります。これは非常に面倒に見えます。 2番目の質問は、追加方法に関するものです。文字列オブジェクトを集約するアレイリストにファイルオブジェクトを追加すると、コンパイラはエラープロンプトを生成しませんが、これは必要なものではありません。
したがって、Java 5から始めて、ArrayListを使用して、使用するときに型パラメーター(タイプパラメーター)を追加できます。このタイプパラメーターは、ArrayListの要素タイプを示すために使用されます。次のコードに示すように、型パラメーターの導入は、上記の2つの問題を解決します。
ArrayList <String> s = new ArrayList <String>(); S.Add( "ABC"); string s = s.get(0); // S.Add(123)をキャストする必要はありません。 //コンピレーションエラー、文字列オブジェクトを追加することしかできません...
上記のコードでは、コンパイラがArrayListのタイプパラメーター文字列を「知っている」後、キャストとタイプチェックを完了します。
2。一般的なクラス
いわゆるジェネリッククラスは、1つ以上のタイプパラメーターを備えたクラスです。例えば:
パブリッククラスペア<t、u> {private t first;プライベートUセカンド; publicペア(t first、u second){this.first = first; this.second = second; } public t getFirst(){return first; } public u getSecond(){return second; } public void setfirst(t newValue){first = newValue; }}上記のコードでは、汎用クラスペアのタイプパラメーターはtとuであり、クラス名の後に角度ブラケットに配置されていることがわかります。ここでは、Tとはタイプを表す最初のタイプの文字を意味します。一般的に使用されるのは、E(要素)、K(キー)、V(値)などです。もちろん、これらの文字を使用してタイプパラメーターを参照しないこともまったく問題ありません。
一般的なクラスをインスタンス化する場合、タイプパラメーターを特定のタイプに置き換えるだけで、ペア<t、u>クラスのインスタンス化など、これを行うことができます。
ペア<string、integer> pair = new pair <string、integer>();
3。一般的な方法
いわゆる一般的な方法は、型パラメーターを備えた方法です。一般的なクラスまたは通常のクラスで定義できます。例えば:
public class arrayalg {public static <t> t getmiddle(t [] a){return a [a.length / 2]; }}上記のコードのgetmiddleメソッドは汎用的な方法であり、定義された形式は、型変数が修飾子の後に配置され、戻り型の前に配置されることです。上記の一般的なメソッドは、さまざまな種類の配列に対して呼び出されることがわかります。これらの配列のタイプが制限されていることがわかっている場合、過負荷で実装することもできますが、エンコーディング効率ははるかに低くなります。上記の一般的な方法を呼び出すための例コードは次のとおりです。
string [] strings = {"aa"、 "bb"、 "cc"};
文字列middle = arrayalg.getMiddle(names);
4。型変数の制限
場合によっては、一般的なクラスまたは一般的な方法がタイプパラメーターをさらに制限したいと考えています。たとえば、特定のクラスまたは特定のインターフェイスを実装するクラスのみのサブクラスにのみできるタイプパラメーターを定義する場合。関連する構文は次のとおりです。
<tはBoundingType>を拡張します(BoundingTypeはクラスまたはインターフェイスです)。 BoundingTypeが1つ以上ある場合があります。「&」を使用して接続するだけです。
5。ジェネリックの実装を理解します
実際、仮想マシンの観点からは、「ジェネリック」の概念はありません。たとえば、上記で定義した一般的なクラスペアは、仮想マシンでこのように見えます(つまり、bytecodeにコンパイルされた後):
public class pair {private object first;プライベートオブジェクトセカンド; publicペア(Object first、object second){this.first = first; this.second = second; } public Object getFirst(){return first; } public Object getSecond(){return second; } public void setfirst(object newValue){first = newValue; } public void setSecond(object newValue){second = newValue; }}上記のクラスは、型消去によって取得され、ペアジェネリッククラスに対応する生のタイプです。タイプ消去とは、すべてのタイプパラメーターをBoundingTypeに置き換えることを意味します(制限が追加されていない場合は、オブジェクトに置き換えます)。
paile.javaをコンパイルした後、「Javap -c -s paile」と入力して取得することを確認できます。
上記の図の「記述子」との線は、対応する方法の署名です。たとえば、4行目から、ペアコンストラクターの2つの正式なパラメーターがタイプ消去後にオブジェクトになっていることがわかります。
一般的なクラスペアは仮想マシンの生型になるため、GetFirstメソッドはオブジェクトオブジェクトを返し、コンパイラの観点からは、このメソッドはクラスをインスタンス化するときに指定されたタイプパラメーターのオブジェクトを返します。実際、キャスティング作業を完了するのに役立つのはコンパイラです。言い換えれば、コンパイラは、ペアジェネリッククラスのGetFirstメソッドにコールを2つの仮想マシンの命令に変換します。
1つ目は、Objectオブジェクトを返すRaw TypeメソッドGetFirstへの呼び出しです。 2番目の命令は、指定した型パラメータータイプに返されたオブジェクトオブジェクトをキャストします。
タイプ消去は、次の一般的な方法などの一般的な方法でも発生します。
public static <tは同等の> t min(t [] a)を拡張します
編集後、タイプが消去された後、次のようになります。
public static comparable min(同等の[] a)
メソッドのタイプ消去はいくつかの問題を引き起こす可能性があります。次のコードを検討してください。
class dateIntervalは、paile <date、date> {public void setSecond(date second){if(second.compareto(getFirst())> = 0){super.setsecond(second); }} ...}上記のコードがタイプごとに消去された後、次のようになります。
class dateInterval拡張ペア{public void setSecond(date second){...} ...}DateIntervalクラスでは、次のように(タイプ消去後)、ペアクラスから継承されたセットセカンドのメソッドもあります。
public void setSecond(オブジェクト2番目)
これで、この方法には、dateintervalによってオーバーライドされたセットセカンドメソッドとは異なるメソッドシグネチャ(異なる形式パラメーター)があることがわかります。したがって、2つの異なる方法ですが、これらの2つの方法は異なる方法であるべきではありません(オーバーライドするため)。次のコードを検討してください。
DateInterval interval = new dateInterval(...); pair <date、date> pair = interval; date adate = new.setsecond(adate);
上記のコードから、ペアは実際にDateIntervalオブジェクトを指しているため、DateIntervalのセットセカンド方式を呼び出す必要があることがわかります。ここでの問題は、ポリ型との対立を消去するタイプがあることです。
この問題が発生する理由を並べ替えましょう。ペアは以前にタイプペア<日付、日付>として宣言されていたため、このクラスは仮想マシンに1つの「セットセカンド(オブジェクト)」メソッドしかないようです。したがって、実行すると、仮想マシンは、ペアが実際にDateIntervalオブジェクトを指すことを発見しますが、DateIntervalの「SetSecond(Object)」を呼び出しますが、DateIntervalクラスには「SetSecond(Date)」メソッドのみがあります。
この問題の解決策は、コンパイラによってdateintervalでブリッジメソッドを生成することです。
public void setsecond(object second){setSecond((date)second);}6。注意すべきこと
(1)型パラメーターを基本型でインスタンス化することはできません
つまり、次の声明は違法です。
paile <int、int> pair = new pair <int、int>();
ただし、代わりに対応するパッケージタイプを使用できます。
(2)一般的なクラスインスタンスをスローまたはキャプチャすることはできません
ジェネリッククラス拡張機能は違法であるため、一般的なクラスインスタンスをスローまたはキャプチャすることはできません。ただし、例外宣言でタイプパラメーターを使用することは合法です。
public static <t extends throwable> void dowork(t t)throws t {try {...} catch(throwable realcause){t.initcause(realcause); tを投げる; }}(3)パラメーター化された配列は違法です
Javaでは、オブジェクト[]アレイは、任意の配列の親クラスにすることができます(すべての配列は、定義されたときに要素タイプを指定する親クラスの配列に上方に変換できるためです)。次のコードを検討してください。
string [] strs = new String [10]; object [] objs = strs; obj [0] = new date(...);
上記のコードでは、親クラス(オブジェクト)タイプを満たすオブジェクトに配列要素を割り当てますが、元のタイプ(ペア)とは異なり、コンパイル時間で渡すことができ、ArrayStoreException例外は実行時にスローされます。
上記の理由に基づいて、Javaが次のステートメントを介して一般的な配列を宣言および初期化できるとします。
ペア<文字列、string> []ペア= new pair <string、string> [10];
次に、仮想マシンがタイプ消去を実行した後、ペアは実際にペア[]アレイになり、オブジェクト[]アレイに上方に変換できます。この時点で、ペア<日付、日付>オブジェクトを追加すると、コンパイル時間チェックとランタイムチェックを渡すことができます。私たちの当初の意図は、この配列ストアペア<string、string>オブジェクトだけを置くことです。これにより、エラーを見つけるのが困難になります。したがって、Javaでは、上記のステートメントフォームを介して一般的な配列を宣言および初期化することはできません。
一般的な配列は、次のステートメントを使用して宣言して初期化できます。
ペア<string、string> []ペア=(ペア<string、string> [])new Pair [10];
(4)タイプ変数をインスタンス化することはできません
タイプ変数は、「new t(...)」、「new t [...]」、「t.class」などの形式では使用できません。 Javaがこれを行うことを禁止する理由は簡単です。タイプの消去があるため、「new T(...)」のようなステートメントは「新しいオブジェクト(...)」になりますが、これは通常私たちの意味ではありません。 「新しいT [...]」への呼び出しを次のステートメントに置き換えることができます。
arrays =(t [])new Object [n];
(5)一般的なクラスの静的コンテキストでは、タイプ変数を使用できません
ここで一般的なクラスを強調していることに注意してください。なぜなら、上記のArrayalgクラスのGetmiddleメソッドなど、静的なジェネリックメソッドは通常のクラスで定義できるためです。そのようなルールの理由から、次のコードを検討してください。
パブリッククラスの人々<t> {public static t name; public static t getname(){...}}同時に、メモリに複数の人のクラスインスタンスがあるかもしれないことを知っています。今すぐメモリに<string>オブジェクトと人<integer>オブジェクトがあると、クラスの静的変数と静的メソッドがすべてのクラスインスタンスで共有されているとします。問題は、名前の文字列タイプまたは整数タイプですか?このため、Javaでは、一般的なクラスの静的コンテキストで使用されることは許可されていません。
7.タイプワイルドカード
タイプのワイルドカードを導入する前に、最初に2つのポイントを紹介します。
(1)学生が人のサブクラスであると仮定しますが、ペア<学生、学生>はペア<人、人>のサブクラスではなく、それらの間に「is-a」の関係はありません。
(2)Pair <T、T>とその元のタイプペアの間には「IS-A」関係があります。ペア<t、t>は、いずれの場合でもペアタイプに変換できます。
次に、この方法を検討してください。
public static void printName(pair <people、people> p){people p1 = p.getfirst(); System.out.println(p1.getName()); //人のクラスがgetNameインスタンスメソッドを定義していると仮定します}上記の方法では、Pair <Student、Student>、Pair <People、People>のパラメーターを同時に渡すことができるようにしたいのですが、2つの間に「is-a」の関係はありません。この場合、Javaはソリューションを提供します。ペアを使用<?フォーマルパラメーターのタイプとして人を拡張します。つまり、ペア<学生、学生>、ペア<ピープル、ピープル>の両方がペアのサブクラスと見なすことができます<?人を拡張します>。
「<?extends BoundingType>」のように見えるコードは、ワイルドカード文字のサブタイプ制限と呼ばれます。これに対応するのは、ワイルドカード文字のスーパータイプの制限ですが、フォーマットは次のとおりです。 Super BoundingType>。
次のコードを考えてみましょう。
ペア<Student>学生= new Pair <Student>(Student1、Student2); Pair <?人を拡張します> wildchards = dustomen; wildchards.setfirst(people1);
上記のコードの3行目は、WildChardsがペアであるため、エラーを報告します<?人の> extends>オブジェクト、およびそのセットファーストメソッドとgetFirstメソッドは次のとおりです。
void setfirst(?人を拡張)?人々を拡張するgetFirst()
SetFirstメソッドの場合、コンパイラはどのタイプの正式なパラメーターがどのようなものか(人々のサブクラスであることが知られています)を知りません。 Peopleオブジェクトを渡そうとすると、コンパイラは人と正式なパラメーターが「IS-A」であるかどうかを判断できないため、SetFirstメソッドを呼び出すとエラーが報告されます。 WildChardsのGetFirstメソッドを呼び出すことは合法です。なぜなら、それは人々のサブクラスを返し、人々のサブクラスを「常に人々だ」と言っているからです。 (いつでもサブクラスオブジェクトを親オブジェクトに変換できます)
WildCard SuperTypeの制限の場合、Calling Setterメソッドは合法ですが、Calling Getterメソッドは違法です。
サブタイプの制限とスーパータイプの制限に加えて、The Infinite WildCardと呼ばれるワイルドカードもあります。これは次のようになります。このことはいつ使用しますか?このシナリオを考慮してください。メソッドを呼び出すと、getpairsメソッドを返します。これにより、ペア<t、t>オブジェクトのセットが返されます。その中には、ペア<学生、生徒>、およびペア<教師、教師>オブジェクトがあります。 (学生クラスと教師クラスの間に相続関係はありません)明らかに、この場合、サブタイプの制限とスーパータイプの制限の両方を使用できません。現時点では、このステートメントを使用して解決できます。
ペア<?> []ペア= getPairs(...);