2日前に同僚のコードレビューを行いました。私はJava Genericsをよく把握していないと感じたので、本「Execful Java」1を取り出して、関連する章を見ました。項目24:排除されていない警告セクションのセクションでは、著者は、アレイリストクラスのpublic <t> t [] toarray(t [] a)メソッドを例として使用して、変数に@supresswarningsアノテーションを使用する方法を説明します。
ArrayListは一般的なクラスで、このように宣言されています。
javapublic class arraylist <e> extends abstractlist <e> explments list <e>、randomAccess、cloneable、java.io.serializable
このクラスのtoArray(t [] a)メソッドは一般的な方法であり、このように宣言され実装されています。
@suppresswarnings( "unchecked")public <t> t [] toarray(t [] a){if(a.length <size)// aのランタイムタイプの新しい配列を作成しますが、私の内容:return.copyof(elementData、a.getclass() > size)a [size] = null; return a;}この方法は、実際にはコレクションインターフェイスで宣言されています。 ArrayListを使用して使用することがよくあるため、ここではArrayListを例として使用します。
1なぜ別のタイプとして宣言されているのですか?
私の質問は、なぜこの方法はアレイリストのタイプEの代わりにタイプtを使用するのですか?つまり、なぜこの方法はこのように宣言されていないのですか:
javapublic e [] toarray(e [] a);
タイプが同じ場合、パラメータータイプエラーはコンパイル中に見つかります。タイプが異なる場合、ランタイムエラーを簡単に生成できます。たとえば、次のコード:
// arrayListList <String> strlist = new ArrayList <String>(); strlist.add( "abc"); strlist.add( "xyz"); //現在のstrlistを番号配列に変換します。次のステートメントには、編集エラーがないことに注意してください。 number [] numarray = strlist.toarray(new number [0]);
上記のコードを実行し、6行目はjava.lang.ArrayStoreExceptionの例外をスローします。
ToArrayメソッドがタイプEを使用する場合、ステートメント2はコンパイルエラーを生成します。コンピレーションエラーは、ランタイムエラーよりも友好的です。さらに、ジェネリックの主な目的は、編集中にタイプ変換エラー(ClassCastException)を排除することです。この方法は逆です。これは大きなバグですか? Javaのバグに遭遇しましたが、まだ信じられません。
インターネットをチェックした後、この問題は何度も議論されています2、3、4。
2は柔軟性を向上させることができます
この宣言はより柔軟であり、現在のリストの要素をより一般的なタイプの配列に変換できます。たとえば、現在のリストタイプは整数であり、その要素を番号配列に変換できます。
リスト<integer> intlist = new arraylist <integer>(); intlist.add(1); intlist.add(2); number [] numarray = intlist.toarray(new number [0]);
この方法がタイプEとして宣言されている場合、上記のコードにはコンパイルエラーがあります。この方法を次のように宣言する方が適切なようです。
javapublic <t super e> t [] toarray(t [] a);
ただし、<tスーパーE>のような構文はJavaには存在しません。そして、たとえそれが存在していても、アレイでは機能しません。また、この方法を使用する場合、たとえtがeの親クラスであっても、tがeと同じであっても、Java.lang.ArrayStoreException 5、6、7を完全に回避できないからです。次の2つのコードをご覧ください。最初のコードでは、tはeの親クラスであり、2番目のコードでは、tはEと同じです。
コード1:
list <integer> intlist = new ArrayList <Integer>(); intlist.add(1); intlist.add(2); float [] floatarray = new float [2]; // floatは数字のサブクラスです。そのため、float []は数字のサブクラスです[] numarray = floatArray;
コード2:
リスト<number> intlist = new arrayList <number>(); //リストのタイプは番号です。ただし、数字は抽象クラスであり、サブクラスintlist.add(new Integer())のインスタンスのみを保存することができます。 intlist.add(new Integer()); float [] floatarray = new float []; // floatは数字のサブクラスです。そのため、float []は数字のサブクラスです[] numarray = floatarray;
上記の例外はすべてこの事実によって引き起こされます。AがBの親クラスである場合、aはb []の親クラスです。 Javaのすべてのクラスはオブジェクトから継承され、オブジェクト[]はすべての配列の親クラスです。
この投稿8は、この方法のタイプがEとして宣言されたとしても、ArrayStoreExceptionを回避できないことを示しています。
この例外は、この方法のドキュメントにも記載されています。
ArrayStoreException指定された配列のランタイムタイプが、このリストのすべての要素のランタイムタイプのスーパータイプではない場合。
3 Java 1.5の前にバージョンと互換性があります
この方法は、Javaにジェネリックを導入する前に登場しました(ジェネリックはJDK1.5で導入されました)。その時に宣言されました:
javapublic object [] toarray(object [] a)
ジェネリックが表示された後、多くのクラスとメソッドがジェネリックになります。この方法は、次のように宣言します。
javapublic <t> t [] toarray(t [] a)
この宣言は、Java 1.5より前のバージョンと互換性があります。
4もう少し言葉
この方法には、配列パラメーターが必要です。この配列の長さが現在のリストのサイズ以上である場合、リスト内の要素は配列に保存されます。この配列の長さが現在のリストのサイズよりも小さい場合、新しい配列が作成され、現在のリストの要素が新しく作成されたアレイに保存されます。効率を改善するには、可能であれば、合格した配列の長さがリストのサイズ以上である必要があります。この方法で新しい配列の作成を避ける必要があります。
list <integer> intlist = new ArrayList <Integer>(); intlist.add(); intlist.add(); //配列の貼り付け、その長さは数字[] numarray = intlist.toarray(new number []); //ステートメント//配列に貼り付け、その長さはintlist番号の長さに等しくなります[] numarray = intlist.toarray(new番号[intlist.size()]); //声明
さらに、パラメーターとしての配列をnullにすることはできません。そうしないと、nullpointerexceptionがスローされます。
脚注:
1
効果的なJava(第2版)
2
リンク
3
リンク
4
リンク
5
リンク
6
リンク
7
リンク
8
リンク
9
リンク
10
リンク
作成:2016-04-06水21:14
EMACS 24.5.1(ORGモード8.2.10)
検証します
上記のコンテンツは、編集者によって紹介されたJava ArrayList.ToArray(T [])メソッドのパラメータータイプがEの代わりにTである理由です。