Javaで配列を扱う際、実行時に「ArrayStoreException」というエラーが発生することがあります。これは、宣言された型とは異なる型の要素を配列に代入したときに発生する実行時例外です。たとえば、String[]
型の配列にInteger
型の値を入れようとすると、見た目は正しくても裏側で型の不整合が起きているため、実行時にエラーが発生します。
このエラーはJavaの「配列の共変性(covariance)」という仕組みに起因しており、宣言型と実体型が異なっていてもコンパイルは通るため、初心者にとっては原因がわかりにくいエラーのひとつです。
この記事では、「ArrayStoreException」の仕組み、よくある発生パターンとその対処法、さらに安全な代替手段まで、丁寧に解説していきます。
ArrayStoreExceptionとは?
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
このようなメッセージが出力された場合、「配列の要素に代入されたオブジェクトの型が配列の実体型と互換性がなかった」という意味になります。コードを書いている段階ではコンパイルが通ってしまい、実行して初めてエラーが明らかになります。
この例外は、特に以下のようなケースで頻出します:
- Object型の配列に実体がString[]などの具体的な型で作成されている場合
- 継承関係にあるクラスの配列に、別のサブクラスのインスタンスを代入した場合
よくあるエラーのパターンと解決策
ケース1:Object型配列と実体型の不一致
NGコード
Object[] arr = new String[3];
arr[0] = "hello";
arr[1] = 123; // 実行時にArrayStoreException
解説
このコードでは、変数arr
はObject[]型で宣言されていますが、実体はString[]型です。共変性によりこの代入はコンパイル上許可されますが、123はStringではないため、配列の整合性が保たれず、実行時に例外がスローされます。
修正例①:Object配列として統一する
Object[] arr = new Object[3];
arr[0] = "hello";
arr[1] = 123; // OK
修正例②:配列全体をString型で揃える
String[] arr = new String[3];
arr[0] = "hello";
arr[1] = "world"; // OK
ケース2:継承関係にあるクラスでの配列代入
クラス定義
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
NGコード
Animal[] animals = new Dog[2];
animals[0] = new Dog(); // OK
animals[1] = new Cat(); // ArrayStoreException
解説
宣言はAnimal[]
ですが、実際にはDog[]
として生成されているため、Cat
を代入しようとすると整合性が崩れて実行時に例外が発生します。
修正例①:実体と宣言をAnimal[]で一致させる
Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat(); // OK
修正例②:Dog[]にはDogのみを入れる
Dog[] dogs = new Dog[2];
dogs[0] = new Dog();
// dogs[1] = new Cat(); // コンパイル時に検出される
補足:配列の共変性とそのリスク
Javaの配列は共変です。つまり、String[]
型の配列はObject[]
としても扱うことができます。しかし、この仕組みによって、実行時に型の不整合が発生しやすくなります。
String[] strs = new String[2];
Object[] objs = strs; // OK
objs[0] = 100; // ArrayStoreException
共変性によって柔軟に見えるこの設計は、実際には実行時まで型の整合性が保証されないというリスクを持っています。対照的に、ジェネリクスは不変性(invariance)を持つため、異なる型への代入を許可せず、より安全なコードが書けます。
より安全な代替:コレクションを使う
配列の代わりに、ArrayList
やList
などのコレクションを使用することで、型安全性を高めることができます。ジェネリクスを利用することで、コンパイル時に不正な型の代入がブロックされるため、実行時エラーを未然に防げます。
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // コンパイルエラーになる
また、コレクションはサイズ変更が可能なため、柔軟性という点でも配列より優れている場合が多いです。
ArrayStoreExceptionを防ぐためのポイントまとめ
- 配列を使うときは、宣言と実体の型を一致させる
- Object配列や親クラス型の配列を使うときは、代入するインスタンスの型に注意する
- 共変性によって一見安全に見えても、実行時にはエラーが起こる可能性があると理解しておく
- IntelliJ IDEA や Eclipse などの IDE の警告や補完を活用する
- 配列に固執せず、ジェネリクス付きのコレクションの使用を検討する
まとめ
- ArrayStoreExceptionは、Javaの配列に不適切な型を代入したときに実行時に発生するエラー
- コンパイル時には気づけないため、宣言と実体の型の違いに常に注意する必要がある
- 共変性による柔軟さは便利だが、実行時エラーのリスクも併せ持つ
- 型安全性を高めたい場合は、コレクションとジェネリクスを活用することが有効
次回予告
次回は、Javaの型推論(varなどによる型の自動推定)や、ジェネリクス(Listのように型を明示する仕組み)を使用した際に発生する「incompatible types」エラーについて解説します。
初心者が間違いやすい型の不一致問題を、丁寧に解説していきますのでお楽しみに!
コメント