メモ: 誤った型の要素の代入で発生するArrayStoreException

先日のどう書くで、「Javaの配列に対する代入のバグ的挙動」というのが話題に出ました。

何の話か気になったので調べたところ、ArrayStoreExceptionのことのようですね。

こういうやつです。

import org.junit.Test;

class Mother {
}

class Child extends Mother {
}

public class ArrayStoreExceptionTest {
	@Test
	public void スーパークラスの配列変数にはスーパークラスのインスタンスを代入できる() throws Exception {
		Mother[] array = new Mother[2];
		array[0] = new Mother();
	}

	@Test
	public void スーパークラスの配列変数にはサブクラスのインスタンスを代入できる() throws Exception {
		Mother[] array = new Mother[2];
		array[0] = new Child();
	}

	@Test
	public void サブクラスの配列変数にはサブクラスのインスタンスを代入できる() throws Exception {
		Mother[] array = new Child[2];
		array[0] = new Child();
	}

	@Test(expected = ArrayStoreException.class)
	public void サブクラスの配列変数にはスーパークラスのインスタンスを代入できない() throws Exception {
		Mother[] array = new Child[2]; // 配列のアップキャストが発生(ここまではOK)
		array[0] = new Mother(); // 代入先の要素が実際にはサブクラスなので、型が違うため、エラーになる
	}
}

4つ目のテストで例外が発生します。

継承関係にある2つのクラスがあります(MotherとChild)。まず、親クラスの配列変数を宣言し、子クラスのインスタンスで初期化します。次に、要素に、親クラスのインスタンスを代入します。

この時点では、コンパイルエラーにはなりませんが、実行するとエラーになります。

理由は、コメントに書いたとおりの認識です。

正直、実際に遭遇したことはまだありません。Javaを自分の数千倍の量書いている人も、同じことを言っていました。配列を使うときには、1つの型に決めて使うというスタイルが、この例外の発生を自然と防いでいるのだと思われます。

# 以下、参考。

例外が投げられるのは、サブクラスの配列への参照を、スーパークラスの配列の参照型変数に代入した場合、つまり「配列をアップキャスト」した場合である。この場合、代入時にもアップキャストが行われるため、コンパイルエラーは発生しないが、型が異なるため、実行時にArrayStoreException例外が投げられることになる。

ArrayStoreExceptionとは : JavaA2Z