1.ジェネリックの概念を提案する(なぜジェネリックが必要なのか)?
まず、次の短いコードを見てみましょう。
public class generictest {public static void main(string [] args){list list = new ArrayList(); list.add( "qqyumidi"); list.add( "コーン"); list.add(100); for(int i = 0; i <list.size(); i ++){string name =(string)list.get(i); // 1 System.out.println( "name:" + name); }}}リストタイプのコレクションを定義し、最初に2つの文字列タイプ値を追加してから、整数型値を追加します。これは、現時点ではデフォルトのタイプのリストがオブジェクトであるため、完全に許可されています。後続のループでは、以前のリストに整数型値またはその他のエンコード理由を追加するのを忘れていたため、// 1に似たエラーがあるのは簡単です。コンピレーション段階は正常であるため、「java.lang.classcastexception」例外が実行時に表示されます。したがって、このようなエラーは、エンコードプロセス中に検出するのが困難です。
上記のエンコードプロセス中に、2つの主な問題があることがわかりました。
1.コレクションにオブジェクトを入れたとき、コレクションはこのオブジェクトのタイプを覚えていません。このオブジェクトが再びコレクションから取り出されると、変更されたオブジェクトのコンパイルタイプがオブジェクトタイプになりますが、ランタイムタイプはまだ独自のタイプです。
2。したがって、// 1のコレクション要素を取り出す場合、人為的に強制されたタイプを特定のターゲットタイプに変換する必要があり、「java.lang.classcastexception」例外を簡単に確認できます。
コレクションにコレクションを覚えておくようにする方法はありますか?コンピレーション中に問題がない限り、それを達成するには、実行時に「java.lang.classcastexception」例外はありませんか?答えは、ジェネリックを使用することです。
2。ジェネリックとは何ですか?
ジェネリック、つまり「パラメーター化されたタイプ」。パラメーターに関しては、最もよく知られていることは、メソッドを定義するときに具体的なパラメーターを使用し、このメソッドを呼び出すときに実際のパラメーターを渡すことです。では、パラメーター化タイプをどのように理解していますか?名前が示すように、メソッドの変数パラメーターと同様に、元の特定のタイプのタイプをパラメーター化することを意味します。この時点で、タイプはパラメーター形式(タイプ形式パラメーターと呼ばれる)として定義され、使用/呼び出されたときに特定のタイプ(タイプの実際のパラメーター)が渡されます。
少し複雑に思えます。まず、一般的なライティングを使用して上記の例を見てみましょう。
public class generictest {public static void main(string [] args){ /* list list = new ArrayList(); list.add( "qqyumidi"); list.add( "コーン"); list.add(100); */ list <string> list = new ArrayList <String>(); list.add( "qqyumidi"); list.add( "コーン"); //list.add(100); // 1の[int i = 0; i <list.size(); i ++){string name = list.get(i); // 2 System.out.println( "name:" + name); }}}一般的なライティングを使用した後、整数型オブジェクトを// 1に追加すると、コンパイルエラーが発生します。 List <String>を通じて、List Collectionに文字列タイプの要素のみを含めることができることが直接制限されているため、// 2にタイプをキャストする必要はありません。現時点では、コレクションは要素のタイプ情報を覚えており、コンパイラは文字列タイプであることを確認できるためです。
上記の一般的な定義を組み合わせることで、list <string>では、文字列はタイプパラメーターであることがわかります。つまり、対応するリストインターフェイスにはタイプの正式なパラメーターが含まれている必要があることがわかります。さらに、get()メソッドの返品結果は、この正式なパラメータータイプ(つまり、対応する受信型パラメーター)です。リストインターフェイスの特定の定義を見てみましょう。
パブリックインターフェイスリスト<e>拡張コレクション<e> {int size(); boolean isempty(); Boolean contains(object o); iterator <e> iterator(); object [] toarray(); <t> t [] toarray(t [] a);ブールアディング(e e);ブール除去(オブジェクトO); Boolean containsall(collection <?> c); Boolean Addall(collection <?extends e> c); Boolean addall(int index、collection <?extends e> c); Boolean Removeall(collection <?> c); boolean resemall(collection <?> c); void clear(); Boolean Equals(オブジェクトO); int hashcode(); e get(int index); e set(int index、e element); void add(int index、e element); e remody(int index); int indexof(object o); int lastindexof(object o); listiterator <e> listiterator(); listiterator <e> listiterator(int index);リスト<e> sublist(int fromindex、int toindex);}リストインターフェイスに汎用定義が採用された後、<e>は特定のタイプパラメーターを受信できるタイプの正式なパラメーターを表すことがわかります。 Eが表示されるこのインターフェイス定義では、外部から受け入れられた同じタイプパラメーターが受け入れられていることを意味します。
当然、ArrayListはリストインターフェイスの実装クラスであり、その定義フォームは次のとおりです。
このことから、ソースコードの観点から、整数型オブジェクトが// 1で誤ってコンパイルされ、// 2で取得されたタイプが直接文字列タイプである理由を理解します。
Public class arrayList <e> extends AbstractList <e> explention List <e>、randomAccess、cloneable、java.io.serializable {public boolean add(e){ensurecapacityinternal(size + 1); // increments modcount !! elementData [size ++] = e; trueを返します。 } public e get(int index){rangecheck(index); checkforcomodification(); return arraylist.this.elementdata(offset + index); } //...その他の特定の定義プロセスを配信}3.一般的なインターフェイス、汎用クラス、一般的な方法をカスタマイズします
上記のコンテンツから、誰もがジェネリックの特定の操作プロセスを理解しています。また、インターフェイス、クラス、および方法をジェネリックを使用して定義し、それに応じて使用できることも知られています。はい、具体的に使用すると、一般的なインターフェイス、汎用クラス、一般的な方法に分けることができます。
カスタムジェネリックインターフェイス、汎用クラス、および一般的なメソッドは、上記のJavaソースコードのリストと配列と類似しています。次のように、最も単純な一般的なクラスとメソッドの定義を見ていきます。
public class generictest {public static void main(string [] args){box <string> name = new box <string>( "corn"); System.out.println( "name:" + name.getData()); }} class box <t> {private t data; public box(){} public box(t data){this.data = data; } public t getData(){return data; }}一般的なインターフェイス、汎用クラス、一般的なメソッドを定義するプロセスでは、T、E、K、Vなどの一般的なパラメーターは、外部使用から渡されたタイプパラメーターを受け取るため、一般的な正式なパラメーターを表すためによく使用されます。したがって、さまざまなタイプの着信パラメーターについて、対応するオブジェクトインスタンスのタイプは生成されますか?
public class generictest {public static void main(string [] args){box <string> name = new box <string>( "corn"); box <integer> age = new Box <Integer>(712); System.out.println( "name class:" + name.getClass()); // com.qqyumidi.box system.out.println( "age class:" + age.getclass()); // com.qqyumidi.box System.out.println(name.getClass()== age.getClass()); // 真実 }}このことから、一般的なクラスを使用すると、異なる一般的な引数が渡されますが、本当の意味では異なるタイプが生成されないことがわかりました。メモリに異なる一般的な引数を渡す一般的なクラスは1つだけです。つまり、それはまだ元の最も基本的なタイプです(この例のボックス)。もちろん、論理的には、複数の異なる汎用タイプとして理解できます。
その理由は、Javaのジェネリックの概念の目的は、コードコンピレーション段階でのみ機能するためです。編集プロセス中、ジェネリック結果を正しくチェックした後、ジェネリックの関連情報が消去されます。つまり、成功したクラスファイルには、一般的な情報は含まれていません。汎用情報はランタイムフェーズに入りません。
これは1つの文で要約されています。一般的なタイプは、論理的に複数の異なるタイプと見なされ、実際には同じ基本タイプです。
4。タイプワイルドカード
上記の結論に続いて、box <number>とbox <integer>は実際には両方のボックスタイプであることがわかります。今、私たちは質問を探求し続ける必要があります。したがって、論理的には、box <number>およびbox <integer>を親子関係を持つ一般的なタイプと見なすことができますか?
この問題を明確にするために、次の例を見てみましょう。
public class generictest {public static void main(string [] args){box <number> name = new box <number>(99); box <integer> age = new Box <Integer>(712); getData(name); //メソッドgetData(box <number>)in type generictestは//引数に適用されない(box <integer>)getData(age); // 1} public static void getData(box <number> data){system.out.println( "data:" + data.getData()); }}エラーメッセージがCode // 1に表示されることがわかりました:T YPE GenerictestのメソッドgetData(box <number>)は、引数には適用できません(box <integer>)。明らかに、情報を促すことにより、box <number>は、ボックス<integer>の親クラスと論理的に見なすことができないことがわかります。それで、理由は何ですか?
public class generictest {public static void main(string [] args){box <integer> a = new box <integer>(712); box <number> b = a; // 1 box <float> f = new Box <Float>(3.14f); B.SetData(F); // 2} public static void getData(box <number> data){system.out.println( "data:" + data.getData()); }} class box <t> {private t data; public box(){} public box(t data){setData(data); } public t getData(){return data; } public void setData(t data){this.data = data; }}この例では、// 1および// 2にエラーメッセージがあります。ここでは、反防止法を使用して説明できます。
box <number>がボックス<integer>の親クラスと見なされると仮定すると、// 1および// 2にエラープロンプトはありません。その後、問題が発生します。 getData()メソッドを介してデータを取得するときは、どのタイプですか?整数?フロート?または番号?さらに、プログラミングプロセスで制御できない順序により、必要に応じてタイプの判断を下し、タイプの変換が実行される必要があります。明らかに、これはジェネリックのアイデアと矛盾しているため、論理的には、box <number>は、ボックス<integer>の親クラスと見なすことはできません。
さて、「タイプワイルドカード」の最初の例を振り返ってみましょう。特定のエラープロンプトのより深い理由を知っています。それで、それを解決する方法は?本社は新しい機能を定義できます。これは明らかにJavaの多型概念に反しているため、Box <integer>とBox <number>の両方の親クラスを表すために論理的に使用できる参照タイプが必要であるため、Wildcardのタイプが生まれました。
タイプのワイルドカードは、特定のタイプの引数の代わりに一般的に使用されます。これはタイプパラメーターであり、型パラメーターではないことに注意してください。ボックス<?>は、論理的にはすべてのボックス<integer>、box <number> ...などの親クラスです。したがって、そのような要件を満たすための一般的な方法を定義できます。
public class generictest {public static void main(string [] args){box <string> name = new box <string>( "corn"); box <integer> age = new Box <Integer>(712); box <number> number = new Box <number>(314); getData(name); getdata(age); getData(number); } public static void getData(box <?> data){system.out.println( "data:" + data.getData()); }}時々、私たちはまた、ワイルドカードの上下のタイプについても聞くかもしれません。正確にはどうですか?
上記の例では、getData()と同様の機能を機能させるメソッドを定義する必要があるが、タイプの引数にはさらに制限があります。それは数字クラスとそのサブクラスのみである可能性があります。現時点では、タイプのワイルドカードの上限が必要です。
public class generictest {public static void main(string [] args){box <string> name = new box <string>( "corn"); box <integer> age = new Box <Integer>(712); box <number> number = new Box <number>(314); getData(name); getdata(age); getData(number); // getuppernumberdata(name); // 1 getuppernumberdata(age); // 2 getuppernumberdata(number); // 3} public static void getData(box <?> data){system.out.println( "data:" + data.getData()); } public static void getuppernumberdata(box <?extends number> data){system.out.println( "data:" + data.getData()); }}この時点で、明らかに、Code // 1での呼び出しはエラーメッセージが表示され、Call at // 2 // 3が正常になります。
タイプのワイルドカードの上限は、ボックス<の形式で定義されます。番号>を拡張します。それに応じて、タイプのワイルドカードの下限はボックスの形です<?スーパー番号>、そしてその意味は、ワイルドカードのタイプの上限の正反対です。ここではあまり説明しません。
5。追加の章
この記事の例は、主にジェネリックのいくつかのアイデアを説明するために引用されており、必ずしも実用的な使いやすさを持っているわけではありません。さらに、ジェネリックに関しては、あなたが最も使用しているのはコレクションにあると思います。実際、実際のプログラミングプロセスでは、ジェネリックを使用して開発を簡素化し、コードの品質を十分に確保できます。注意すべきことの1つは、Javaにはいわゆるジェネリック配列がないということです。
ジェネリックにとって、最も重要なことは、その背後にあるアイデアと目的を理解することです。
上記は、Java Genericsに関する知識と情報の編集です。今後も関連情報を追加し続けます。このサイトへのご支援ありがとうございます!