#導入
こんにちは。けちょんです。
皆さん、ArrayList使ってますか?
もちろん私もよく使います。
1つ予想外なことがあったので紹介します。
#クイズ
以下ソースで実行時例外が発生します。
それはどこでしょう。
String[] array = { "banana", "apple", "melon" }; // 配列を作成
List<String> list1 = Arrays.asList(array); // 配列からリストの作成
List<String> list2 = new ArrayList<>();
list1.stream().forEach(e -> list2.add(e)); // list1と同じ要素を持つリストを作成
list1.add("lemon"); // list1に追加
list2.add("lemon"); // list2に追加
System.out.print(list1); // list1の結果を出力
System.out.print(list2); // list2の結果を出力
※streamに不慣れな方に言っておくと、streamの行ではありません。
↓
↓
↓
↓
↓
↓
↓
↓
↓
正解は、list1.add("lemon")
です!
以下例外が発生します。
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.add(AbstractList.java:153)
at java.base/java.util.AbstractList.add(AbstractList.java:111)
at Main2.main(Main2.java:44)
ちなみにlist2.add("lemon")
は正常に実行されます。
#なぜ例外が発生する?
##まず、それぞれクラスを見てみましょう
System.out.println(list1.getClass()); // class java.util.Arrays$ArrayList
System.out.println(list2.getClass()); // class java.util.ArrayList
どちらもArrayListです。
が、list1はArraysクラスの内部クラスのArrayListです
つまり、ArrayListクラスは二つあるのです。(どや)
##java.util.Arrays$ArrayListの中身は?
Arraysの中を覗いてみましょう。
長いので書ききれませんが、クラス名の宣言はこのようになっています。
・・・
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
・・・
内部クラスで定義されていますね。
そして、ArrayListの中ではaddメソッドは実装されていません。
そのため、親クラスのAbstractListを見に行きます。(エラーメッセージで出力されていたクラスですね)
・・・
public boolean add(E e) {
add(size(), e);
return true;
}
・・・
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
・・・
犯人はこいつですね!!
呼び出すとUnsupportedOperationExceptionを投げるように実装されています。
##なぜこんな実装に、、、
AbstractListは文字通りabstractクラスであるため、実装前提のクラスです。
そのため、継承せずに呼び出すと例外を投げるよう実装しているようです。
いや、インターフェースにするなり、asListメソッドの実装を変えたり、やりようがある気はしますが、、、
詳しい人教えてください、、、
#まとめ
java.utilには、以下二つのArrayListが存在する
①class java.util.Arrays$ArrayList
②class java.util.ArrayList
①はaddメソッドを呼び出すと例外が発生するので要注意