[解決済み] 汎用配列の作成方法 重複
質問
ジェネリックと配列の関係がよくわからないのですが。
ジェネリック型で配列の参照を作ることができるのですが。
private E[] elements; //GOOD
しかし、ジェネリックタイプの配列オブジェクトを作成することができません。
elements = new E[10]; //ERROR
しかし、それは動作します。
elements = (E[]) new Object[10]; //GOOD
どのように解決するのですか?
配列とジェネリクスを混同してはいけません。両者は相性が悪いのです。配列とジェネリック型では、型チェックを行う方法に違いがあります。配列は再定義されると言いますが、ジェネリックはそうではありません。その結果、配列とジェネリクスで作業していると、このような違いが見えてくるのです。
配列は共変であり、ジェネリックはそうではない。
どういうことでしょうか?次の代入が有効であることは、もうお分かりでしょう。
Object[] arr = new String[10];
基本的には
Object[]
のスーパータイプです。
String[]
の上位型であり、なぜなら
Object
のスーパータイプだからです。
String
. これはジェネリックでは真ではありません。そのため、以下の宣言は無効であり、コンパイルされません。
List<Object> list = new ArrayList<String>(); // Will not compile.
理由は、ジェネリックが不変だからです。
型チェックを強制する。
ジェネリックは、コンパイル時の型チェックを強化するためにJavaに導入されました。そのため、ジェネリック型は、以下の理由により、実行時にいかなる型情報も持ちません。
型消去
. そのため
List<String>
は静的な型である
List<String>
という静的な型を持っていますが、動的な型は
List
.
しかし、配列は構成要素の型の実行時型情報を一緒に運びます。実行時に、配列はArray Store checkを使用して、実際の配列の型と互換性のある要素を挿入しているかどうかをチェックします。そのため、以下のようなコードになります。
Object[] arr = new String[10];
arr[0] = new Integer(10);
はコンパイルはうまくいきますが、ArrayStoreCheckの結果、実行時に失敗します。ジェネリックでは、これは不可能です。コンパイラは、上記のように、このような参照の作成を避けることによって、コンパイル時のチェックを提供し、実行時例外を防ごうとします。
では、ジェネリック配列の作成の問題点は何でしょうか?
構成要素の型が 型パラメータ , a 具体的なパラメタライズド型 または 境界付きワイルドカードパラメータ化された型 は 型安全でない .
以下のようなコードを考えてみましょう。
public <T> T[] getArray(int size) {
T[] arr = new T[size]; // Suppose this was allowed for the time being.
return arr;
}
の型は
T
の型は実行時には分からないので、作成される配列は実際には
Object[]
. ですから、実行時の上記のメソッドは次のようになります。
public Object[] getArray(int size) {
Object[] arr = new Object[size];
return arr;
}
では、このメソッドを次のように呼び出したとします。
Integer[] arr = getArray(10);
ここで問題です。今、あなたは
Object[]
の参照に
Integer[]
. 上記のコードはコンパイルはうまくいきますが、実行時に失敗します。
これが汎用配列の作成が禁止されている理由です。
なぜタイプキャストなのか
new Object[10]
を
E[]
は動作しますか?
さて、最後の疑問ですが、なぜ以下のコードが動作するのでしょうか?
E[] elements = (E[]) new Object[10];
上記のコードは、上で説明したのと同じ意味合いを持っています。お気づきのように、コンパイラはあなたに チェックされていないキャストの警告 という警告が表示されます。つまり、実行時にキャストが失敗する可能性があるということです。例えば、上記のメソッドにそのようなコードがあった場合。
public <T> T[] getArray(int size) {
T[] arr = (T[])new Object[size];
return arr;
}
で、このように呼び出す。
String[] arr = getArray(10);
のようにすると、実行時にClassCastExceptionが発生して失敗します。つまり、この方法は常に動作するわけではありません。
型の配列を作成するのはどうでしょうか?
List<String>[]
?
問題は同じです。型消去のため
List<String>[]
は何もありませんが
List[]
. では、このような配列の作成が許されていた場合、どのようなことが起こりうるか見てみましょう。
List<String>[] strlistarr = new List<String>[10]; // Won't compile. but just consider it
Object[] objarr = strlistarr; // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.
これで、ArrayStoreExceptionが投げられるはずなのに、上記の場合のArrayStoreCheckは実行時に成功します。それは、両方の
List<String>[]
と
List<Integer>[]
がコンパイルされると
List[]
にコンパイルされます。
では、境界のないワイルドカードのパラメタライズドタイプの配列を作成することができるのでしょうか?
はい、その理由は
List<?>
は再利用可能な型だからです。そしてそれは、型が全く関連付けられていないので、理にかなっています。つまり、型消去の結果として失うものは何もないのです。ですから、このような型の配列を作成することは完全に型安全です。
List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>(); // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine
上記のケースはどちらも問題ありません。
List<?>
は汎用型である
List<E>
. ですから、実行時にArrayStoreExceptionを発行することはありません。このケースはraw型の配列でも同じです。raw型も再利用可能な型であるため、配列を作成する際に
List[]
.
つまり、reifiableな型の配列しか作れず、non-reifiableな型の配列は作れないということになります。上記のすべてのケースで、配列の宣言は問題なく、配列の作成は
new
演算子による配列の作成が問題となります。しかし、これらの参照型の配列を宣言しても意味がありません。なぜなら、これらの参照型は
null
(
非束縛型は無視する
).
を回避する方法はありますか?
E[]
?
はい、配列の作成は
Array#newInstance()
メソッドで作成できます。
public <E> E[] getArray(Class<E> clazz, int size) {
@SuppressWarnings("unchecked")
E[] arr = (E[]) Array.newInstance(clazz, size);
return arr;
}
型キャストが必要なのは、このメソッドが
Object
. しかし、それが安全なキャストであることを確認することができます。ですから、その変数に@SuppressWarningsを使用することもできます。
関連
-
undefined[sonar] sonar:デフォルトのスキャンルール
-
コンストラクタDate()が未定義である問題
-
[解決済み] 配列からArrayListを作成する
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] Mavenを使用して、依存関係を持つ実行可能なJARを作成するにはどうすればよいですか?
-
[解決済み] Javaで汎用配列を作成する方法は?
-
[解決済み] メソッドの戻り値の型を汎用的にするにはどうすればよいですか?
-
[解決済み] C#でジェネリックメソッドからNULLを返すにはどうしたらいいですか?
-
[解決済み] ジェネリックメソッドを数値型に制限する制約がありますか?
-
[解決済み】TをEnumに拘束するGenericメソッドの作成
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
mvn' は、内部または外部のコマンド、操作可能なプログラムまたはバッチファイルとして認識されません。
-
Eclipseは、ポップアップA Java Exception has occurred.を実行し、エラーException in threadの解決策を報告します。
-
スレッド "main "での例外 java.util.NoSuchElementException in Java 問題解決済み
-
javaの非静的メソッドを静的に参照することができない
-
Springの設定でxsdファイルのバージョン番号を設定しない方が良い理由
-
メモ帳でJavaプログラムをコンパイルして実行すると、Could not find or load main class ...というエラーが表示される。解決方法
-
StringBuilderが投げるArrayIndexOutOfBoundsExceptionの探索
-
BindException: アドレスはすでに使用中です:バインドエラー解決
-
ecplise プロンプトが表示されます。"選択したものは起動できません。" "最近の起動はありません。"
-
[解決済み] Javaで汎用配列を作成する方法は?